﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace rFDevLib {
    public struct RPMData {
        // Standard
        public double RPM; // Engine speed, in RPM
        public double Coast; // Engine torque at 0% throttle, in nm
        public double Torque; // Engine torque at 100% throttle, in nm

        // Non-standard
        public double Power; // Engine power in hp

        public void FillPower() {
            this.Power = (this.RPM * this.Torque) / (1.35581795 * 5252.0);
        }

        public double GetTorqueOutput( double throttle ) {
            return Coast + ((Torque - Coast) * throttle);
        }

        public RPMData( double _RPM, double _Coast, double _Torque ) {
            RPM = _RPM;
            Coast = _Coast;
            Torque = _Torque;

            Power = 0.0;
            FillPower();
        }

        public RPMData( string Line ) {
            char[] delimiterChars = { '(', ' ', ',', ')' };
            string[] words = Line.Replace( " ", "" ).Split( delimiterChars );

            CultureInfo c = new CultureInfo( "en-US" );

            RPM = Double.Parse( words[1], c );
            Coast = Double.Parse( words[2], c );
            Torque = Double.Parse( words[3], c );
            Power = 0.0;

            FillPower();
        }

        public string ToString() {
            return "RPMTorque=(" + RPM.ToString() + "," + Coast.ToString() + "," + Torque.ToString() + ")";
        }
    }

    public class rF1Engine {
        public string fileName = string.Empty;

        public List<rFParam> m_params;
        public List<RPMData> m_torqueCurve;

        public rF1Engine() {
            m_params = new List<rFParam>();
            m_torqueCurve = new List<RPMData>();
        }

        public double GetMaxRPM() {
            foreach ( rFParam a in m_params ) {
                if ( a.name == "RevLimitRange" ) {
                    return a.GetDouble( 0 ) + (a.GetDouble( 1 ) * (a.GetDouble( 2 ) - 1.0));
                }
            }

            return 0.0;
        }

        public rFParam GetParam( string name ) {
            foreach ( rFParam a in m_params ) {
                if ( a.name == name )
                    return a;
            }

            return new rFParam();
        }

        public double GetTorque( double RPM, double throttle ) {
            RPMData p1 = new RPMData(0, 0, 0), p2 = new RPMData(10, 0, 0);
            for ( int i = 0; i < m_torqueCurve.Count; i++ ) {
                if ( m_torqueCurve[i].RPM > RPM  || i == m_torqueCurve.Count - 1) {
                    p1 = m_torqueCurve[i - 1];
                    p2 = m_torqueCurve[i];
                    break;
                }
            }

            double ratio = (RPM - p1.RPM) / (p2.RPM - p1.RPM);

            double torque = (p2.GetTorqueOutput( throttle ) * ratio) + (p1.GetTorqueOutput( throttle ) * (1.0 - ratio));

            return torque;
        }

        public void OpenFile( string filePath ) {
            StreamReader r = new StreamReader(filePath);
            string line;

            m_params.Clear();
            m_torqueCurve.Clear();

            while ( r.EndOfStream == false ) {
                line = r.ReadLine();
                if ( line.Trim().Length == 0) {
                    // This line's empty, so is to be ignored.
                    // TODO: find a way to restart the loop here and now?
                } else if ( line.Substring( 0, Math.Min(9, line.Length) ).ToLower() == "rpmtorque" ) {
                    // Torque curve data
                    m_torqueCurve.Add( new RPMData( line.Trim() ) );
                } else {
                    // Other params
                    m_params.Add( new rFParam( line.Trim() ) );
                }
            }

            r.Close();

            fileName = filePath.Split('\\').Last();

            if ( m_torqueCurve.Count() == 0 ) {
                throw new Exception( "This is not a valid rF1 engine!" );
            }

            return;
        }

        public string GetPowerInfo() {
            double maxhp = 0.0; // double.MinValue;
            double hp = 0.0;
            double rpm = 0.0;

            for ( int i = 0; i < m_torqueCurve.Count; i++ ) {
                RPMData a = m_torqueCurve[i];
                hp = (a.RPM * a.Torque) / (1.35581795 * 5252.0);

                if ( hp > maxhp ) {
                    maxhp = hp;
                    rpm = a.RPM;
                }
            }

            return maxhp.ToString( "F2" ) + "hp @ " + Math.Floor( rpm ).ToString() + "rpm";
        }
    }
}
